<!--
  Excel导入导出组件 - 支持导入和导出功能
  依赖：Element Plus, xlsx
  
  使用示例：
  <ExcelImportExporter
    :data="tableData"
    :columns="columns"
    filename="数据管理"
    @import-success="handleImportSuccess"
    @export-success="handleExportSuccess"
  />
-->
<template>
  <div class="excel-import-exporter">
    <!-- 导入区域 -->
    <div class="import-section" v-if="showImport">
      <el-upload
        ref="uploadRef"
        :auto-upload="false"
        :show-file-list="false"
        :accept="acceptedFileTypes"
        :on-change="handleFileChange"
        :before-upload="beforeUpload"
      >
        <el-button :type="importButtonType" :loading="isImporting">
          <el-icon><Upload /></el-icon>
          {{ isImporting ? "导入中..." : importButtonText }}
        </el-button>
      </el-upload>

      <!-- 导入模板下载 -->
      <el-button
        v-if="showDownloadTemplate"
        type="info"
        text
        @click="downloadTemplate"
        style="margin-left: 8px"
      >
        <el-icon><Download /></el-icon>
        下载模板
      </el-button>
    </div>

    <!-- 分隔符 -->
    <el-divider v-if="showImport && showExport" direction="vertical" />

    <!-- 导出区域 -->
    <div class="export-section" v-if="showExport">
      <el-input
        v-model="filename"
        :placeholder="filenamePlaceholder"
        :style="{ width: inputWidth }"
      />
      <el-button
        :type="exportButtonType"
        :loading="isExporting"
        :disabled="disabled || data.length === 0"
        @click="handleExport"
        style="margin-left: 12px"
      >
        <el-icon><Download /></el-icon>
        {{ isExporting ? "导出中..." : exportButtonText }}
      </el-button>
    </div>

    <!-- 导入进度对话框 -->
    <el-dialog
      v-model="showImportProgress"
      title="导入进度"
      width="400px"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
    >
      <div style="text-align: center">
        <el-progress :percentage="importProgress" />
        <p style="margin-top: 16px">正在解析数据，请稍候...</p>
      </div>
    </el-dialog>

    <!-- 导出进度对话框 -->
    <el-dialog
      v-model="showExportProgress"
      :title="progressTitle"
      width="400px"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
    >
      <div style="text-align: center">
        <el-progress :percentage="exportProgress" />
        <p style="margin-top: 16px">{{ progressText }}</p>
      </div>
    </el-dialog>

    <!-- 导入数据预览对话框 -->
    <el-dialog
      v-model="showPreviewDialog"
      title="导入数据预览"
      width="80%"
      :close-on-click-modal="false"
    >
      <div>
        <div style="margin-bottom: 16px">
          <el-alert
            :title="`共解析到 ${previewData.length} 条数据，请确认后导入`"
            type="info"
            show-icon
            :closable="false"
          />
        </div>

        <!-- 预览表格 -->
        <el-table
          :data="previewData.slice(0, 10)"
          border
          max-height="400"
          style="width: 100%"
        >
          <el-table-column
            v-for="col in columns"
            :key="col.key"
            :prop="col.key"
            :label="col.title"
            :width="col.width ? col.width * 8 : undefined"
            show-overflow-tooltip
          />
        </el-table>

        <div
          v-if="previewData.length > 10"
          style="margin-top: 8px; color: #909399; font-size: 12px"
        >
          注：仅显示前10条数据作为预览
        </div>
      </div>

      <template #footer>
        <div style="text-align: right">
          <el-button @click="cancelImport">取消</el-button>
          <el-button
            type="primary"
            @click="confirmImport"
            :loading="isConfirmingImport"
          >
            确认导入
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch } from "vue";
import { Upload, Download } from "@element-plus/icons-vue";
import { ElMessage, ElNotification, type UploadFile } from "element-plus";
import * as XLSX from "xlsx";

// 列配置接口
export interface ExcelColumn {
  key: string;
  title: string;
  width?: number;
  formatter?: (value: any, row: any) => string;
  validator?: (value: any) => boolean | string; // 数据验证器
  required?: boolean; // 是否必需
}

// Props接口
interface Props {
  data: any[]; // 要导出的数据
  columns: ExcelColumn[]; // 列配置
  filename?: string; // 默认文件名
  sheetName?: string; // 工作表名称

  // 显示控制
  showImport?: boolean; // 显示导入功能
  showExport?: boolean; // 显示导出功能
  showDownloadTemplate?: boolean; // 显示模板下载
  showPreview?: boolean; // 显示导入预览

  // 按钮配置
  importButtonText?: string; // 导入按钮文字
  exportButtonText?: string; // 导出按钮文字
  importButtonType?: "primary" | "success" | "info" | "warning" | "danger";
  exportButtonType?: "primary" | "success" | "info" | "warning" | "danger";

  // 其他配置
  disabled?: boolean; // 是否禁用
  inputWidth?: string; // 输入框宽度
  filenamePlaceholder?: string; // 文件名输入框占位符
  acceptedFileTypes?: string; // 接受的文件类型
  maxFileSize?: number; // 最大文件大小(MB)

  // 进度对话框
  showProgressDialog?: boolean; // 是否显示进度对话框
  progressTitle?: string; // 进度对话框标题
  progressText?: string; // 进度文字
}

const props = withDefaults(defineProps<Props>(), {
  filename: "",
  sheetName: "Sheet1",
  showImport: true,
  showExport: true,
  showDownloadTemplate: true,
  showPreview: true,
  importButtonText: "导入Excel",
  exportButtonText: "导出Excel",
  importButtonType: "primary",
  exportButtonType: "success",
  disabled: false,
  inputWidth: "200px",
  filenamePlaceholder: "请输入文件名",
  acceptedFileTypes: ".xlsx,.xls",
  maxFileSize: 10,
  showProgressDialog: true,
  progressTitle: "导出进度",
  progressText: "正在导出数据，请稍候...",
});

// Emits接口
interface Emits {
  // 导出事件
  (e: "export-start", filename: string): void;
  (e: "export-success", filename: string): void;
  (e: "export-error", error: any): void;

  // 导入事件
  (e: "import-start", file: File): void;
  (e: "import-success", data: any[]): void;
  (e: "import-error", error: any): void;
  (e: "import-preview", data: any[]): void;
}

const emit = defineEmits<Emits>();

// 响应式数据
const filename = ref(props.filename);
const uploadRef = ref();

// 导出相关状态
const isExporting = ref(false);
const exportProgress = ref(0);

// 导入相关状态
const isImporting = ref(false);
const importProgress = ref(0);
const isConfirmingImport = ref(false);
const showPreviewDialog = ref(false);
const previewData = ref<any[]>([]);
const currentFile = ref<File | null>(null);

// 计算属性
const showExportProgress = computed(
  () => props.showProgressDialog && isExporting.value
);
const showImportProgress = computed(
  () => isImporting.value && !showPreviewDialog.value
);

// 监听props变化
watch(
  () => props.filename,
  (newVal) => {
    filename.value = newVal;
  }
);

// 内置Excel导出方法
const exportToExcel = (options: {
  data: any[];
  columns: ExcelColumn[];
  filename?: string;
  sheetName?: string;
}) => {
  const {
    data,
    columns,
    filename = `导出数据_${new Date().toLocaleDateString()}`,
    sheetName = "Sheet1",
  } = options;

  try {
    // 构建表头
    const headers = columns.map((col) => col.title);

    // 构建数据行
    const rows = data.map((row) => {
      return columns.map((col) => {
        const value = row[col.key];
        return col.formatter ? col.formatter(value, row) : value;
      });
    });

    // 合并表头和数据
    const sheetData = [headers, ...rows];

    // 创建工作表
    const worksheet = XLSX.utils.aoa_to_sheet(sheetData);

    // 设置列宽
    const colWidths = columns.map((col) => ({
      wch: col.width || 15,
    }));
    worksheet["!cols"] = colWidths;

    // 创建工作簿
    const workbook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);

    // 导出文件
    XLSX.writeFile(workbook, `${filename}.xlsx`);

    ElMessage.success("导出成功");
  } catch (error) {
    console.error("Excel导出失败:", error);
    ElMessage.error("导出失败，请重试");
    throw error;
  }
};

// 导出方法
const handleExport = async () => {
  if (props.data.length === 0) {
    ElMessage.warning("没有可导出的数据");
    return;
  }

  const exportFilename =
    filename.value.trim() || `数据导出_${new Date().toLocaleDateString()}`;

  isExporting.value = true;
  exportProgress.value = 0;

  try {
    emit("export-start", exportFilename);

    // 模拟进度
    exportProgress.value = 30;
    await new Promise((resolve) => setTimeout(resolve, 200));

    exportProgress.value = 70;
    await new Promise((resolve) => setTimeout(resolve, 200));

    // 执行导出
    exportToExcel({
      data: props.data,
      columns: props.columns,
      filename: exportFilename,
      sheetName: props.sheetName,
    });

    exportProgress.value = 100;
    emit("export-success", exportFilename);
  } catch (error) {
    console.error("Excel导出失败:", error);
    emit("export-error", error);
  } finally {
    isExporting.value = false;
    exportProgress.value = 0;
  }
};

// 文件上传前验证
const beforeUpload = (file: File) => {
  const isValidType = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
  if (!isValidType) {
    ElMessage.error("只支持 .xlsx 或 .xls 格式的文件");
    return false;
  }

  const isValidSize = file.size / 1024 / 1024 < props.maxFileSize;
  if (!isValidSize) {
    ElMessage.error(`文件大小不能超过 ${props.maxFileSize}MB`);
    return false;
  }

  return true;
};

// 文件选择变化
const handleFileChange = (file: UploadFile) => {
  if (file.raw) {
    currentFile.value = file.raw;
    handleImport(file.raw);
  }
};

// 导入处理
const handleImport = async (file: File) => {
  isImporting.value = true;
  importProgress.value = 0;

  try {
    emit("import-start", file);

    importProgress.value = 30;
    await new Promise((resolve) => setTimeout(resolve, 300));

    // 读取Excel文件
    const arrayBuffer = await file.arrayBuffer();
    const workbook = XLSX.read(arrayBuffer, { type: "array" });

    importProgress.value = 60;
    await new Promise((resolve) => setTimeout(resolve, 200));

    // 获取第一个工作表
    const worksheet = workbook.Sheets[workbook.SheetNames[0]];
    const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

    if (jsonData.length < 2) {
      throw new Error("文件内容为空或格式不正确");
    }

    importProgress.value = 80;

    // 解析数据
    const headers = jsonData[0] as string[];
    const rows = jsonData.slice(1) as any[][];

    // 将数据转换为对象数组
    const parsedData = rows.map((row, index) => {
      const rowData: any = {};
      props.columns.forEach((col, colIndex) => {
        // 根据列标题匹配数据
        const headerIndex = headers.findIndex((h) => h === col.title);
        if (headerIndex !== -1) {
          rowData[col.key] = row[headerIndex];
        }
      });
      return { ...rowData, _rowIndex: index + 2 }; // 保存原始行号用于错误提示
    });

    importProgress.value = 100;

    // 数据验证
    const validationErrors = validateImportData(parsedData);
    if (validationErrors.length > 0) {
      ElNotification.error({
        title: "数据验证失败",
        message: `发现 ${validationErrors.length} 个错误，请检查数据格式`,
        duration: 0,
      });
      console.error("验证错误:", validationErrors);
      return;
    }

    // 显示预览或直接导入
    if (props.showPreview) {
      previewData.value = parsedData;
      showPreviewDialog.value = true;
      emit("import-preview", parsedData);
    } else {
      emit("import-success", parsedData);
      ElMessage.success(`成功导入 ${parsedData.length} 条数据`);
    }
  } catch (error) {
    console.error("Excel导入失败:", error);
    ElMessage.error(
      `导入失败: ${error instanceof Error ? error.message : "未知错误"}`
    );
    emit("import-error", error);
  } finally {
    isImporting.value = false;
    importProgress.value = 0;
    // 清空上传组件
    uploadRef.value?.clearFiles();
  }
};

// 数据验证
const validateImportData = (data: any[]) => {
  const errors: Array<{ row: number; column: string; message: string }> = [];

  data.forEach((row, index) => {
    props.columns.forEach((col) => {
      const value = row[col.key];

      // 必需字段验证
      if (
        col.required &&
        (value === undefined || value === null || value === "")
      ) {
        errors.push({
          row: row._rowIndex || index + 2,
          column: col.title,
          message: "必需字段不能为空",
        });
      }

      // 自定义验证器 - 总是调用验证器，让验证器自己决定如何处理空值
      if (col.validator) {
        const result = col.validator(value);
        if (result !== true) {
          errors.push({
            row: row._rowIndex || index + 2,
            column: col.title,
            message: typeof result === "string" ? result : "数据格式不正确",
          });
        }
      }
    });
  });

  return errors;
};

// 确认导入
const confirmImport = async () => {
  isConfirmingImport.value = true;
  try {
    emit("import-success", previewData.value);
    ElMessage.success(`成功导入 ${previewData.value.length} 条数据`);
    showPreviewDialog.value = false;
  } catch (error) {
    ElMessage.error("导入失败");
  } finally {
    isConfirmingImport.value = false;
  }
};

// 取消导入
const cancelImport = () => {
  showPreviewDialog.value = false;
  previewData.value = [];
  currentFile.value = null;
};

// 下载导入模板
const downloadTemplate = () => {
  try {
    // 创建模板数据
    const headers = props.columns.map((col) => col.title);
    const sampleRow = props.columns.map((col) => {
      // 根据字段类型生成示例数据
      if (col.key.includes("date") || col.key.includes("time")) {
        return "2024-01-01";
      } else if (col.key.includes("phone")) {
        return "13800138000";
      } else if (col.key.includes("email")) {
        return "example@email.com";
      } else if (col.key.includes("status")) {
        return "1";
      } else {
        return `示例${col.title}`;
      }
    });

    const templateData = [headers, sampleRow];

    // 创建工作表
    const worksheet = XLSX.utils.aoa_to_sheet(templateData);

    // 设置列宽
    const colWidths = props.columns.map((col) => ({
      wch: col.width || 15,
    }));
    worksheet["!cols"] = colWidths;

    // 创建工作簿
    const workbook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workbook, worksheet, "导入模板");

    // 导出模板文件
    const templateName = `${props.sheetName || "数据"}导入模板.xlsx`;
    XLSX.writeFile(workbook, templateName);

    ElMessage.success("模板下载成功");
  } catch (error) {
    console.error("模板下载失败:", error);
    ElMessage.error("模板下载失败");
  }
};

// 暴露方法给父组件
defineExpose({
  export: handleExport,
  setFilename: (name: string) => {
    filename.value = name;
  },
  downloadTemplate,
  clearImport: () => {
    cancelImport();
    uploadRef.value?.clearFiles();
  },
});
</script>

<style lang="scss" scoped>
.excel-import-exporter {
  display: flex;
  align-items: center;
  gap: 8px;

  .import-section,
  .export-section {
    display: flex;
    align-items: center;
  }

  .el-divider--vertical {
    height: 20px;
  }
}
</style>